package log

import (
	"bytes"
	"context"
	"fmt"
	"gitee.com/manoshi/go-util/conf"
	"gorm.io/gorm/logger"
	"gorm.io/gorm/utils"
	"runtime"
	"strconv"
	"strings"
	"time"
)

const (
	LevelDebug = "Debug"
	LevelInfo  = "Info"
	LevelWarn  = "Warn"
	LevelError = "Error"
)

func DebugDo(f func()) {
	if IsDebug() {
		f()
	}
}

func IsDebug() bool {
	ok := false
	switch strings.ToLower(conf.LogLevel) {
	case strings.ToLower(LevelDebug):
		ok = true
		break
	}
	return ok
}

func Debugf(format string, msg ...interface{}) {
	ok := false
	switch strings.ToLower(conf.LogLevel) {
	case strings.ToLower(LevelDebug):
		ok = true
		break
	}
	if ok {
		fileNum := utils.FileWithLineNum()
		print(LevelDebug, fileNum, fmt.Sprintf(format, msg...))
	}
}

func Debug(msg interface{}) {
	ok := false
	switch strings.ToLower(conf.LogLevel) {
	case strings.ToLower(LevelDebug):
		ok = true
		break
	}
	if ok {
		fileNum := utils.FileWithLineNum()
		print(LevelDebug, fileNum, msg)
	}
}

func Infof(format string, msg ...interface{}) {
	ok := false
	switch strings.ToLower(conf.LogLevel) {
	case strings.ToLower(LevelDebug):
		ok = true
		break
	case strings.ToLower(LevelInfo):
		ok = true
		break
	}
	if ok {
		fileNum := utils.FileWithLineNum()
		print(LevelInfo, fileNum, fmt.Sprintf(format, msg...))
	}
}

func Info(msg interface{}) {
	ok := false
	switch strings.ToLower(conf.LogLevel) {
	case strings.ToLower(LevelDebug):
		ok = true
		break
	case strings.ToLower(LevelInfo):
		ok = true
		break
	}
	if ok {
		fileNum := utils.FileWithLineNum()
		print(LevelInfo, fileNum, msg)
	}
}

func Warnf(format string, msg ...interface{}) {
	ok := false
	switch strings.ToLower(conf.LogLevel) {
	case strings.ToLower(LevelDebug):
		ok = true
		break
	case strings.ToLower(LevelInfo):
		ok = true
		break
	case strings.ToLower(LevelWarn):
		ok = true
		break
	}
	if ok {
		fileNum := utils.FileWithLineNum()
		print(LevelWarn, fileNum, fmt.Sprintf(format, msg...))
	}
}

func Warn(msg interface{}) {
	ok := false
	switch strings.ToLower(conf.LogLevel) {
	case strings.ToLower(LevelDebug):
		ok = true
		break
	case strings.ToLower(LevelInfo):
		ok = true
		break
	case strings.ToLower(LevelWarn):
		ok = true
		break
	}
	if ok {
		fileNum := utils.FileWithLineNum()
		print(LevelWarn, fileNum, msg)
	}
}

func Errorf(format string, msg ...interface{}) {
	ok := false
	switch strings.ToLower(conf.LogLevel) {
	case strings.ToLower(LevelDebug):
		ok = true
		break
	case strings.ToLower(LevelInfo):
		ok = true
		break
	case strings.ToLower(LevelWarn):
		ok = true
		break
	case strings.ToLower(LevelError):
		ok = true
		break
	}
	if ok {
		fileNum := utils.FileWithLineNum()
		print(LevelError, fileNum, fmt.Sprintf(format, msg...))
	}
}

func Error(msg interface{}) {
	ok := false
	switch strings.ToLower(conf.LogLevel) {
	case strings.ToLower(LevelDebug):
		ok = true
		break
	case strings.ToLower(LevelInfo):
		ok = true
		break
	case strings.ToLower(LevelWarn):
		ok = true
		break
	case strings.ToLower(LevelError):
		ok = true
		break
	}
	if ok {
		fileNum := utils.FileWithLineNum()
		print(LevelError, fileNum, msg)
	}
}

// Colors
const (
	reset       = "\033[0m"
	red         = "\033[31m"
	green       = "\033[32m"
	yellow      = "\033[33m"
	blue        = "\033[34m"
	magenta     = "\033[35m"
	cyan        = "\033[36m"
	white       = "\033[37m"
	blueBold    = "\033[34;1m"
	magentaBold = "\033[35;1m"
	redBold     = "\033[31;1m"
	yellowBold  = "\033[33;1m"
)

func print(tag, file string, msg interface{}) {
	str := "%s [%d] %s %s %s"
	if conf.LogColorful {
		switch tag {
		case LevelDebug:
			str = "%s [%d] %s %s " + green + "%s" + reset
			break
		case LevelInfo:
			str = "%s [%d] %s %s " + yellow + "%s" + reset
			break
		case LevelWarn:
			str = "%s [%d] %s %s " + magenta + "%s" + reset
			break
		case LevelError:
			str = "%s [%d] %s %s " + red + "%s" + reset
			break
		}
	}
	file = file[strings.LastIndex(file, "/")+1:]
	fmt.Println(fmt.Sprintf(str, formatDateMillTime(time.Now()), getGID(), tag, file, msg))
}
func formatDateMillTime(dataTime time.Time) string {
	return dataTime.Format("2006-01-02 15:04:05.000000")
}

func getGID() uint64 {
	b := make([]byte, 64)
	b = b[:runtime.Stack(b, false)]
	b = bytes.TrimPrefix(b, []byte("goroutine "))
	b = b[:bytes.IndexByte(b, ' ')]
	n, _ := strconv.ParseUint(string(b), 10, 64)
	return n
}

type CustomLogger struct {
}

func (l *CustomLogger) LogMode(level logger.LogLevel) logger.Interface {
	return l
}

func (l *CustomLogger) Info(ctx context.Context, s string, i ...interface{}) {
}

func (l *CustomLogger) Warn(ctx context.Context, s string, i ...interface{}) {
}

func (l *CustomLogger) Error(ctx context.Context, s string, i ...interface{}) {
}

func (l *CustomLogger) Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {
	if conf.LogShowSql {
		sql, rows := fc()
		end := time.Now()
		fileNum := utils.FileWithLineNum()
		msg := fmt.Sprintf("[%v] [rows:%v] %s", end.Sub(begin), rows, sql)
		print(LevelDebug, fileNum, msg)
	}
}
